#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "OneLineTemplate.h"
#include "micro.h"
#define STATE1 1
#define STATE2 2
#define STATE3 3
#define STATE4 4

static size_t GetVarIndex(const char *varName, const SymTable *symTable, size_t symTableLen)
{
    size_t varIndex = 0;
    for (size_t i = 1; i < symTableLen; i++)
    {
        if(0 == strcmp(varName, symTable[i].varName))
        {
            varIndex = i;
            break;
        }
    }
    ld("varIndex = %u", varIndex);
    Return varIndex;
}

OneLineTemplate* OneLineTemplateCreate(const char* text, const SymTable *symTable, size_t symTableLen)
{
    OneLineTemplate *head    = NULL;
    OneLineTemplate *tail    = NULL;
    OneLineTemplate *newNode = NULL;
    size_t textIndex     = 0;
    int    state         = STATE1;
    char   currentChr    = 0;
    char   token[1024]   = {0};
    size_t tokenIndex = 0;

    while((currentChr = text[textIndex++]) != '\0')
    {
        if (STATE1 == state)
        {
            if ('{' != currentChr)
            {
                //commone char, just put it into token
                token[tokenIndex++] = currentChr;
            }
            else
            {
                //about to get a var, first need to save token if needed
                if (0 != tokenIndex)
                {
                    token[tokenIndex] = '\0';
                    newNode = (OneLineTemplate*)malloc(sizeof(OneLineTemplate));
                    if (NULL == newNode)
                    {
                        OneLineTemplateRelase(head);
                        Return NULL;
                    }
                    newNode->dataType = DATATYPE_CONSTTEXT;
                    snprintf(newNode->data.constText, sizeof(newNode->data.constText), "%s", token);
                    newNode->next = NULL;
                    //insert into list;
                    if (NULL != head)
                    {
                        tail->next = newNode;
                        tail       = newNode;
                    }
                    else
                    {
                        head = newNode;
                        tail = head;
                    }
                }
                //reset tokenIndex for further use, and state change to STATE2
                tokenIndex = 0;
                state      = STATE2;
            }
        }
        else if (STATE2 == state)
        {
            if ((currentChr >= 'a' && currentChr <= 'z') ||
                (currentChr >= 'A' && currentChr <= 'Z'))
            {
                //var char, put it into token
                token[tokenIndex++] = currentChr;
                state = STATE3;
            }
            else
            {
                //error, expecting [a-zA-Z]
                le("expecting [a-zA-z], but got %c", currentChr);
                state = STATE4;
            }
        }
        else if(STATE3 == state)
        {
            if ((currentChr >= 'a' && currentChr <= 'z') ||
                    (currentChr >= 'A' && currentChr <= 'Z') ||
                    (currentChr >= '0' && currentChr <= '9'))
            {
                //var char, put it into token
                token[tokenIndex++] = currentChr;
            }
            else if('}' == currentChr)
            {
                //got a var
                token[tokenIndex] = '\0';
                newNode = (OneLineTemplate*)malloc(sizeof(OneLineTemplate));
                if (NULL == newNode)
                {
                    OneLineTemplateRelase(head);
                    Return NULL;
                }
                newNode->dataType = DATATYPE_VARINDEX;
                snprintf(newNode->data.var.varName, sizeof(newNode->data.var.varName), "%s", token);
                newNode->data.var.varIndex = GetVarIndex(token, symTable, symTableLen);
                newNode->next = NULL;
                //insert into list;
                if (NULL != head)
                {
                    tail->next = newNode;
                    tail       = newNode;
                }
                else
                {
                    head = newNode;
                    tail = head;
                }
                //reset tokenIndex
                tokenIndex = 0;
                state = STATE1;
            }
            else
            {
                //error, expecting [a-zA-Z0-9}]
                le("expecting [a-zA-z0-9}], but got %c at index %d", currentChr, textIndex - 1);
                state = STATE4;
            }
        }
        else if (STATE4 == state)
        {
            //error state
            OneLineTemplateRelase(head);
            head = NULL;
            break;
        }
    }
    if (STATE1 == state)
    {
        //about to get a var, first need to save token if needed
        if (0 != tokenIndex)
        {
            token[tokenIndex] = '\0';
            newNode = (OneLineTemplate*)malloc(sizeof(OneLineTemplate));
            if (NULL == newNode)
            {
                OneLineTemplateRelase(head);
                Return NULL;
            }
            newNode->dataType = DATATYPE_CONSTTEXT;
            snprintf(newNode->data.constText, sizeof(newNode->data.constText), "%s", token);
            newNode->next = NULL;
            //insert into list;
            if (NULL != head)
            {
                tail->next = newNode;
                tail       = newNode;
            }
            else
            {
                head = newNode;
                tail = head;
            }
        }
    }
    else
    {
        OneLineTemplateRelase(head);
        Return NULL;
    }
    Return head;
}

void OneLineTemplateRelase(OneLineTemplate *tpl)
{
    OneLineTemplate *current = tpl;
    OneLineTemplate *next    = NULL;
    while(current)
    {
        next = current->next;
        free(current);
        current = next;
    }
}

//TODO: expandTextLen need to be check
int  OneLineTemplateExpand(const OneLineTemplate *tpl, const SymTable *symTable, size_t symTableLen, char *expandText, size_t expandTextLen)
{
    const OneLineTemplate *current = tpl;
    const OneLineTemplate *next    = NULL;
    size_t expandTextIndex         = 0;
    while(current && (expandTextLen > expandTextIndex))
    {
        next = current->next;
        if (DATATYPE_CONSTTEXT == current->dataType)
        {
            ld("current->data.constText = %s", current->data.constText);
            expandTextIndex += snprintf(expandText + expandTextIndex, expandTextLen - expandTextIndex, "%s", current->data.constText);
        }
        else if(current->data.var.varIndex < symTableLen)
        {
            ld("current->data.var.varIndex = %u:%s", current->data.var.varIndex, symTable[current->data.var.varIndex].varValue);
            expandTextIndex += snprintf(expandText + expandTextIndex, expandTextLen - expandTextIndex, "%s", symTable[current->data.var.varIndex].varValue);
        }
        else
        {
            //error, var.varIndex is to not in [0, symTableLen)
            Return -1;
        }
        current = next;
    }
    Return 0;
}

